/*
 * Copyright (c) 2024 iSoftStone Education Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define EEPROM_TIME_MAX_LEN 32
#include "iot_i2c.h"
#include "iot_errno.h"

#include <stdio.h>
#include <string.h>
#include <stddef.h>  // 提供 NULL 和 size_t 定义
#include <stdint.h>
#include <eeprom.h>

/* eeprom对应i2c */
#define EEPROM_I2C_PORT EI2C0_M2

#define EEPROM_I2C_ADDRESS      0x51

// 在iot.h或其他合适位置添加

#define MAX_TIME_STR_LEN       32    // 与receive_time_str长度匹配


/* EEPROM型号：K24C02，2Kbit（256Byte），32页，每页8个字节（Byte） */
#define EEPROM_ADDRESS_MAX      256
#define EEPROM_PAGE             8




#define EEPROM_TIME_MAX_LEN  32  // 每组时间最大长度
#define EEPROM_PTR_ADDR      0xA0 // 存储当前写入指针的地址
#define EEPROM_TIME_START_ADDR 0x00 // 时间存储起始地址

static uint8_t g_current_group = 0; // 当前写入组号


// 初始化时从EEPROM读取指针
void eeprom_init_ptr() {
    eeprom_readbyte(EEPROM_PTR_ADDR, &g_current_group);
    if (g_current_group >= MAX_TIME_GROUP) {
        g_current_group = 0; // 非法值复位
    }

   for (int i = 0; i < MAX_ALARMS; i++) {
        PillTime default_time = {25, 0};
        unsigned int addr = EEPROM_TIME_START_ADDR + i * sizeof(PillTime);
        eeprom_write(addr, (uint8_t*)&default_time, sizeof(PillTime));
        printf("Forced init alarm[%d] to 25:00\n", i);
    }

    g_current_group = 0; // 重置指针
    eeprom_writebyte(EEPROM_PTR_ADDR, g_current_group);
    
}


// 更新指针并保存到EEPROM
void eeprom_update_ptr() {
    g_current_group = (g_current_group + 1) % MAX_TIME_GROUP;
    eeprom_writebyte(EEPROM_PTR_ADDR, g_current_group);
}

// 写入闹钟时间到 EEPROM（指定组）
unsigned int eeprom_write_alarm_time(int group, uint8_t hour, uint8_t minute) {
    if (group < 0 || group >= MAX_ALARMS) return 1;
    PillTime t = { hour, minute };
    unsigned int addr = EEPROM_TIME_START_ADDR + group * sizeof(PillTime);
    return eeprom_write(addr, (uint8_t*)&t, sizeof(PillTime));
}

////////////////////////////////////////////////////////

/***************************************************************
* 函数名称: eeprog_delay_usec
* 说    明: 忙等待usec
* 参    数: 
*       @usec：等待时间，单位：usec
* 返 回 值: 无
***************************************************************/
static inline void eeprog_delay_usec(unsigned int usec)
{
    for (unsigned int i = 0; i < usec; i++)
        HAL_DelayUs(1);
}

/***************************************************************
* 函数名称: eeprom_init
* 说    明: EEPROM初始化
* 参    数: 无
* 返 回 值: 0为成功，反之为失败
***************************************************************/
unsigned int eeprom_init()
{
    if(IoTI2cInit(EEPROM_I2C_PORT, EI2C_FRE_100K) != IOT_SUCCESS) {
        printf("%s, %s, %d: I2c init failed!\n", __FILE__, __func__, __LINE__);
        return IOT_FAILURE;
    }

    return IOT_SUCCESS;
}

/***************************************************************
* 函数名称: eeprom_deinit
* 说    明: EEPROM退出
* 参    数: 无
* 返 回 值: 0为成功，反之失败
***************************************************************/
unsigned int eeprom_deinit()
{
    if(IoTI2cDeinit(EEPROM_I2C_PORT) != IOT_SUCCESS) {
        printf("%s, %s, %d: I2c deinit failed!\n", __FILE__, __func__, __LINE__);
        return IOT_FAILURE;
    }

    return IOT_SUCCESS;
}

/***************************************************************
* 函数名称: eeprom_get_blocksize
* 说    明: EEPROM获取页大小
* 参    数: 无
* 返 回 值: 返回页大小
***************************************************************/
unsigned int eeprom_get_blocksize()
{
    return EEPROM_PAGE;
}

/***************************************************************
* 函数名称: eeprom_readbyte
* 说    明: EEPROM读一个字节
* 参    数: 
*           @addr: EEPROM存储地址
*           @data: 存放EERPOM的数据指针
* 返 回 值: 返回读取字节的长度，反之为错误
***************************************************************/
unsigned int eeprom_readbyte(unsigned int addr, unsigned char *data)
{
    unsigned int ret = 0;
    unsigned char buffer[1];

    /* K24C02的存储地址是0~255 */
    if (addr >= EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr(0x%x) >= EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr, EEPROM_ADDRESS_MAX);
        return 0;
    }

    buffer[0] = (unsigned char)addr;
    
    ret = IoTI2cWrite(EEPROM_I2C_PORT, EEPROM_I2C_ADDRESS, &buffer[0], 1);
    if (ret != IOT_SUCCESS)
    {
        printf("===== Error: I2C write ret = 0x%x! =====\r\n", ret);
        return IOT_FAILURE;
    }
    
    ret = IoTI2cRead(EEPROM_I2C_PORT, EEPROM_I2C_ADDRESS, data, 1);
    if (ret != IOT_SUCCESS)
    {
        printf("===== Error: I2C read ret = 0x%x! =====\r\n", ret);
        return IOT_FAILURE;
    }

    return IOT_SUCCESS;
}

/***************************************************************
* 函数名称: eeprom_writebyte
* 说    明: EEPROM写一个字节
* 参    数: 
*           @addr: EEPROM存储地址
*           @data: 写EERPOM的数据
* 返 回 值: 返回写入数据的长度，反之为错误
***************************************************************/
unsigned int eeprom_writebyte(unsigned int addr, unsigned char data)
{
    unsigned int ret = 0;
    unsigned char buffer[2];

    /* K24C02的存储地址是0~255 */
    if (addr >= EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr(0x%x) >= EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr, EEPROM_ADDRESS_MAX);
        return 0;
    }

    buffer[0] = (unsigned char)(addr & 0xFF);
    buffer[1] = data;

    ret = IoTI2cWrite(EEPROM_I2C_PORT, EEPROM_I2C_ADDRESS, &buffer[0], 2);
    if (ret != IOT_SUCCESS)
    {
        printf("===== Error: I2C write ret = 0x%x! =====\r\n", ret);
        return IOT_FAILURE;
    }

    /* K24C02芯片需要时间完成写操作，在此之前不响应其他操作*/
    eeprog_delay_usec(1000);

    return IOT_SUCCESS;
}
// unsigned int eeprom_write_time(const char *time_str) {
//     if (time_str == NULL || strlen(time_str) >= EEPROM_TIME_MAX_LEN) {
//         printf("Invalid time string\n");
//         return IOT_FAILURE;
//     }

//     unsigned int addr = EEPROM_TIME_START_ADDR;
//     unsigned int ret;

//     // 写入字符串（包括终止符'\0'）
//     for (size_t i = 0; i <= strlen(time_str); i++) {
//         ret = eeprom_writebyte(addr + i, (unsigned char)time_str[i]);
//         if (ret != IOT_SUCCESS) {
//             printf("Failed to write time to EEPROM at addr 0x%x\n", addr + i);
//             return IOT_FAILURE;
//         }
//     }

//     printf("Time saved to EEPROM: %s\n", time_str);
//     return IOT_SUCCESS;
// }

// 修改 eeprom_write_time 函数
unsigned int eeprom_write_time(const char *time_str) {
    if (time_str == NULL || strlen(time_str) >= EEPROM_TIME_MAX_LEN) {
        return IOT_FAILURE;
    }

    // 计算当前组起始地址
    unsigned int start_addr = EEPROM_TIME_START_ADDR + (g_current_group * EEPROM_TIME_MAX_LEN);

    // 写入时间数据（包括终止符）
    for (size_t i = 0; i <= strlen(time_str); i++) {
        if (eeprom_writebyte(start_addr + i, (unsigned char)time_str[i]) != IOT_SUCCESS) {
            return IOT_FAILURE;
        }
    }
    printf("Writing time '%s' to group %d (addr: 0x%x)\n", 
       time_str, g_current_group, 
       EEPROM_TIME_START_ADDR + (g_current_group * EEPROM_TIME_MAX_LEN));

    // 更新写入指针
    eeprom_update_ptr();
    return IOT_SUCCESS;
}

unsigned int eeprom_read_time(char *buffer, size_t size) {
    if (buffer == NULL || size < EEPROM_TIME_MAX_LEN) {
        printf("Invalid buffer\n");
        return IOT_FAILURE;
    }

    unsigned int addr = EEPROM_TIME_START_ADDR;
    unsigned int ret;
    char temp[EEPROM_TIME_MAX_LEN] = {0};

    // 逐个字节读取直到遇到'\0'或超出长度
    for (size_t i = 0; i < EEPROM_TIME_MAX_LEN - 1; i++) {
        unsigned char data;
        ret = eeprom_readbyte(addr + i, &data);
        if (ret != IOT_SUCCESS) {
            printf("Failed to read EEPROM at addr 0x%x\n", addr + i);
            return IOT_FAILURE;
        }

        temp[i] = (char)data;
        if (temp[i] == '\0') {
            break; // 遇到终止符结束
        }
    }

    // 简单验证时间格式
    if (strlen(temp) < 19 || temp[4] != '-' || temp[7] != '-') {
        printf("Invalid time format: %s\n", temp);
        return IOT_FAILURE;
    }

    strncpy(buffer, temp, size - 1);
    buffer[size - 1] = '\0';
    return IOT_SUCCESS;
}


// 新增函数：读取全部5组时间
unsigned int eeprom_read_all_times(char times[MAX_TIME_GROUP][EEPROM_TIME_MAX_LEN]) {
    for (int group = 0; group < MAX_TIME_GROUP; group++) {
        unsigned int start_addr = EEPROM_TIME_START_ADDR + (group * EEPROM_TIME_MAX_LEN);
        for (size_t i = 0; i < EEPROM_TIME_MAX_LEN - 1; i++) {
            unsigned char data;
            if (eeprom_readbyte(start_addr + i, &data) != IOT_SUCCESS) {
                return IOT_FAILURE;
            }
            times[group][i] = (char)data;
            if (times[group][i] == '\0') break;
        }
        times[group][EEPROM_TIME_MAX_LEN - 1] = '\0'; // 确保终止符
    }
    return IOT_SUCCESS;
}

/***************************************************************
* 函数名称: eeprom_writepage
* 说    明: EEPROM写1个页字节
* 参    数: 
*           @addr: EEPROM存储地址，必须是页地址
*           @data: 写EERPOM的数据指针
*           @data_len: 写EEPROM数据的长度，必须是小于1个页大小
* 返 回 值: 返回写入数据的长度，反之为错误
***************************************************************/
unsigned int eeprom_writepage(unsigned int addr, unsigned char *data, unsigned int data_len)
{
    unsigned int ret = 0;
    unsigned char buffer[EEPROM_PAGE + 1];
    
    /* K24C02的存储地址是0~255 */
    if (addr >= EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr(0x%x) >= EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr, EEPROM_ADDRESS_MAX);
        return 0;
    }

    if ((addr % EEPROM_PAGE) != 0) {
        printf("%s, %s, %d: addr(0x%x) is not page addr(0x%x)\n", __FILE__, __func__, __LINE__, addr, EEPROM_PAGE);
        return 0;
    }

    if ((addr + data_len) > EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr + data_len(0x%x) > EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr + data_len, EEPROM_ADDRESS_MAX);
        return 0;
    }

    if (data_len > EEPROM_PAGE) {
        printf("%s, %s, %d: data_len(%d) > EEPROM_PAGE(%d)\n", __FILE__, __func__, __LINE__, data_len, EEPROM_PAGE);
        return 0;
    }

    buffer[0] = addr;
    memcpy(&buffer[1], data, data_len);

    ret = IoTI2cWrite(EEPROM_I2C_PORT, EEPROM_I2C_ADDRESS, &buffer[0], 1 + data_len);
    if (ret != IOT_SUCCESS)
    {
        printf("===== Error: I2C write ret = 0x%x! =====\r\n", ret);
        return IOT_FAILURE;
    }

    /* K24C02芯片需要时间完成写操作，在此之前不响应其他操作*/
    eeprog_delay_usec(1000);

    return data_len;
}

/***************************************************************
* 函数名称: eeprom_read
* 说    明: EEPROM读多个字节
* 参    数: 
*           @addr:      EERPOM存储地址
*           @data:      存放EERPOM的数据指针
*           @data_len:  读取EERPOM数据的长度
* 返 回 值: 返回读取字节的长度，反之为错误
***************************************************************/
unsigned int eeprom_read(unsigned int addr, unsigned char *data, unsigned int data_len) 
{
    unsigned int ret = 0;

    if (addr >= EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr(0x%x) >= EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr, EEPROM_ADDRESS_MAX);
        return 0;
    }

    if ((addr + data_len) > EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr + len(0x%x) > EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr + data_len, EEPROM_ADDRESS_MAX);
        return 0;
    }   

    ret = eeprom_readbyte(addr, data);
    if (ret != IOT_SUCCESS) {
        printf("%s, %s, %d: EepromReadByte failed(%d)\n", __FILE__, __func__, __LINE__, ret);
        return 0;
    }

    if (data_len > 1) {
        ret = IoTI2cRead(EEPROM_I2C_PORT, EEPROM_I2C_ADDRESS, &data[1], data_len - 1);
        if (ret != IOT_SUCCESS)
        {
            printf("===== Error: I2C read ret = 0x%x! =====\r\n", ret);
            return IOT_FAILURE;
        }
    }

    return data_len;
}

/***************************************************************
* 函数名称: eeprom_write
* 说    明: EEPROM写多个字节
* 参    数: 
*           @addr: EEPROM存储地址
*           @data: 写EERPOM的数据指针
*           @data_len: 写EEPROM数据的长度
* 返 回 值: 返回写入数据的长度，反之为错误
***************************************************************/
unsigned int eeprom_write(unsigned int addr, unsigned char *data, unsigned int data_len)
{
    unsigned int ret = 0;
    unsigned int offset_current = 0;
    unsigned int page_start, page_end;
    unsigned char is_data_front = 0;
    unsigned char is_data_back = 0;
    unsigned int len;

    if (addr >= EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr(0x%x) >= EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr, EEPROM_ADDRESS_MAX);
        return 0;
    }

    if ((addr + data_len) > EEPROM_ADDRESS_MAX) {
        printf("%s, %s, %d: addr + len(0x%x) > EEPROM_ADDRESS_MAX(0x%x)\n", __FILE__, __func__, __LINE__, addr + data_len, EEPROM_ADDRESS_MAX);
        return 0;
    }

    /* 判断addr是否是页地址 */
    page_start = addr / EEPROM_PAGE;
    if ((addr % EEPROM_PAGE) != 0) {
        page_start += 1;
        is_data_front = 1;
    }

    /* 判断addr + data_len是否是页地址 */
    page_end = (addr + data_len) / EEPROM_PAGE;
    if ((addr + data_len) % EEPROM_PAGE != 0) {
        page_end += 1;
        is_data_back = 1;
    }

    offset_current = 0;
    
    /* 处理前面非页地址的数据，如果是页地址则不执行 */
    for (unsigned int i = addr; i < (page_start * EEPROM_PAGE); i++) {
        ret = eeprom_writebyte(i, data[offset_current]);
        if (ret != IOT_SUCCESS) {
            printf("%s, %s, %d: EepromWriteByte failed(%d)\n", __FILE__, __func__, __LINE__, ret);
            return offset_current;
        }
        offset_current++;
    }

    /* 处理后续的数据，如果数据长度不足一个Page，则不执行 */
    for (unsigned int page = page_start; page < page_end; page++) {
        len = EEPROM_PAGE;
        if ((page == (page_end - 1)) && (is_data_back)) {
            len = (addr + data_len) % EEPROM_PAGE;
        }
    
        ret = eeprom_writepage(page * EEPROM_PAGE, &data[offset_current], len);
        if (ret != len) {
            printf("%s, %s, %d: EepromWritePage failed(%d)\n", __FILE__, __func__, __LINE__, ret);
            return offset_current;
        }
        offset_current += EEPROM_PAGE;
    }

    return data_len;
}
